<?php

import("data.db.DbAbstractForeign");

class DbAbstractTable {
	
	var $forwardForeigns = array();
	var $backwardForeigns = array();
	var $fildsForStore = array();
	
	var $lingvo = array();
	var $locale;
	
	// properties
	var $id;
	var $userName = "nobody";
	var $groupName = "nobody";
	var $access = "0777";
	
	public function __construct($noinit=false) {
		if (!$noinit)
			$this->initForeigns();
	}
	
	//id
	function setId($id) {$this->id = $id;}
	function getId() {return $this->id;}
	
	function setLingvo($obj) {$this->lingvo = $obj;}
	function getLingvo() {return $this->lingvo;}
	
	function setLocale($obj) {$this->locale = $obj;}
	function getLocale() {return $this->locale;}
	
	function setUserName($val) {$this->userName = $val;}
	function getUserName() {return $this->userName;}
	
	function setGroupName($val) {$this->groupName = $val;}
	function getGroupName() {return $this->groupName;}
	
	function setAccess($val) {$this->access = $val;}
	function getAccess() {return $this->access;}
	
	// Foreign section
	/*
		- forward link (return one object)
		Category -> Catalogue
	 	category_id_catalogue - foreign obj - catalogue_id
	 	
	 	select * from Catalogue where catalogue_id = 'category_id_catalogue'
	 	
	 	function forwardForeignCollection() {
			return array("Catalogue"=>"data.logic.catalogue.Catalogue");
			or
			return array("Catalogue"=>array("data.logic.catalogue.Catalogue","key_index"));
			
			//key_index - name of key field of host table
		}
		
		- backward link (return list of objects)
		Catalogue -> Categories
		catalogue_id - foreign obj - category_id_catalogue
		
		select * from Category where category_id_catalogue = 'catalogue_id'
		
	*/
	function forwardForeignCollection() {
		return array();
	}
	function backwardForeignCollection() {
		return array();
	}
	
	// depr
	function initForeign($arrResult, $className) {
		//$id = $arrResult[$this->_get_FOREIGN($this->getClassName(), $className)];
		//$this->forwardForeigns[$className] = new DbAbstractForeign($this->getClassName(), $className, $id);
	}
	
	function initForeigns() {
		if (!$this->forwardForeigns) {
			$a = $this->forwardForeignCollection();
			foreach ($a as $name=>$class) {
				if (is_array($class)) {
					if ($this->className!=$class[0]) {
						$this->forwardForeigns[$name] = 
							new DbAbstractForeign($this->className, $class[0], $class[1]);
					}
				} else {
					if ($this->className!=$class) {
						$this->forwardForeigns[$name] = 
							new DbAbstractForeign($this->className, $class);
					}
				}
			}
		}
		
		if (!$this->backwardForeigns) {
			$a = $this->backwardForeignCollection();
			foreach ($a as $name=>$class) {
				//if (is_array($class)) {
				//	$this->backwardForeigns[$name] = 
				//		new DbAbstractForeign($this->className, $class[0], $class[1]);
				//} else {
					$this->backwardForeigns[$name] = 
						new DbAbstractForeign($class, $this->className);
				//}
			}
		}
	}
	
	function getForeignId($className) {
		return $this->forwardForeigns[$className]->getId();
	}
	
	
	function fillEntity($arrResult) {
		$this->id = $arrResult[$this->_get_ID()];
		if (isset($arrResult[$this->_get_SUFFIX()."_s_user"]))
			$this->setUserName($arrResult[$this->_get_SUFFIX()."_s_user"]);
		if (isset($arrResult[$this->_get_SUFFIX()."_s_group"]))
			$this->setGroupName($arrResult[$this->_get_SUFFIX()."_s_group"]);
		if (isset($arrResult[$this->_get_SUFFIX()."_s_access"]))
			$this->setAccess($arrResult[$this->_get_SUFFIX()."_s_access"]);
		
		foreach ($this->forwardForeigns as $k=>$fc) {
			$this->forwardForeigns[$k]->setId($arrResult[$this->forwardForeigns[$k]->getForeignKey()]);
		}
		foreach ($this->backwardForeigns as $k=>$bc) {
			$this->backwardForeigns[$k]->setId($arrResult[$this->_get_ID()]);
		}
		
	}
	
	//store
	function fillFields() {
		$this->fildsForStore[$this->_get_SUFFIX()."_s_user"] = "'".$this->getUserName()."'";
		$this->fildsForStore[$this->_get_SUFFIX()."_s_group"] = "'".$this->getGroupName()."'";
		$this->fildsForStore[$this->_get_SUFFIX()."_s_access"] = "'".$this->getAccess()."'";
	}
	
	function store() {
		// TODO: add for forings classes
		$autoincrement_key=false;
		
		$db = SysContext::getConnection();
    	if (!$db) return false;
		
    	$this->fillFields();
    	$result = false;
    	
    	if (strlen($this->getId())>0) {
    		// update
    		$sql_str = "update ";
    		$sql_str .= $this->_get_TABLENAME();
    		$sql_str .= " set ";
    		$f_var = "";
	    	foreach ($this->fildsForStore as $field=>$value) {
	    		$f_var .= (strlen($f_var)>0?",":"").$field."=".$value;
	    	}
	    	$sql_str .= " ".$f_var." where ".$this->_get_ID()."='".$this->getId()."'";
	    	
	    	$rs = $db->execute($sql_str);
  			$result = 0;
    	} else {
    		// insert new
	    	$sql_str = "insert into ";
	    	$sql_str .= $this->_get_TABLENAME();
	    	
	    	$f_var = "";
	    	$f_val = "";
	    	foreach ($this->fildsForStore as $field=>$value) {
	    		$f_var .= (strlen($f_var)>0?",":"").$field;
	    		$f_val .= (strlen($f_val)>0?",":"").$value;
	    	}
	    	
	    	foreach ($this->forwardForeigns as $className=>$foriegn) {
	    		$f_var .= (strlen($f_var)>0?",":"").$foriegn->foreignKey;
	    		if ($foriegn->id) {
	    			// for objects restored from DB and storing again at this moment
    				$f_val .= (strlen($f_val)>0?",":"")."'".$foriegn->id."'";
	    		} else if ($foriegn->container) {
	    			// for new objects
	    			$f_val .= (strlen($f_val)>0?",":"")."'".$foriegn->container->getId()."'";
	    		}
	    	}
	    	
	    	
	    	
	    	if ($autoincrement_key) {
	    		$sql_str .= " (".$f_var.") values(".$f_val.")";
	    	
  				$rs = $db->execute($sql_str);
  				$result = true;
	    	
				$this->id = $db->Insert_ID();
	    	} else {
	    		import("util.CryptUtil");
	    		$newid = CryptUtil::generateKey();
	    		$sql_str .= " (".$this->_get_ID().", ".$f_var.") values('".$newid."',".$f_val.")";
	    	
  				$rs = $db->execute($sql_str);
  				// if no errors
  				$result = true;
	    	
				$this->id = $newid;
	    	}
    	}
//print_r($sql_str);
		return $result;
  		
	}
	
	//remove
	function remove() {
		$db = SysContext::getConnection();
    	if (!$db) return false;
		
    	$result = false;
    	
    	if ($this->getId()>0) {
    		// update
    		$sql_str = "delete from ";
    		$sql_str .= $this->_get_TABLENAME();
    		$sql_str .= " where ";
	    	$sql_str .= $this->_get_ID()."=".$this->getId();
	    	
	    	$rs = $db->execute($sql_str);
	    	$this->setId(null);
  			$result = 0;
    	} else {
    		return true;
    	}
//print_r($sql_str);
		return $result;
  		
	}

	
	// select
	public static function __selectByCriteria($criteria, $className, $local=null) {
		return DbAbstractTable::__select($criteria, 1, 0, null, $className, $local);
	}
	
	public static function __selectAllByCriteria($criteria, $className, $local=null) {
		return DbAbstractTable::__select($criteria, 0, 0, null, $className, $local);
	}
	
	public static function __select($criteria, $limit, $offset, $join, $className, $local=null) {
		$db = SysContext::getConnection();
    	if (!$db) return false;
		
    	$sql_str = "select * from ";
    	$sql_str .= self::_get_TABLENAME($className);
    	
    	if ($join) {
    		$sql_str .= ",".$join;	
    	}
    	
    	
    	if ($criteria || $local) {
    		$sql_str .=  " where ";
    	}
    	if ($criteria) {
    		$sql_str .=  $criteria;
    	}
    	if ($local) {
    		// field for locale
    		$local_fn = self::_get_SUFFIX($className)."_locale";
    		if ($criteria) $sql_str .=  " and ";
    		$sql_str .=  $local_fn."='".$local."'";
    	}
    	

//print_r($sql_str."\n");
  		$rs = $db->execute($sql_str);
 		
  		$result = null;
  		if ($limit==1) {
    		if ($rs && !$rs->EOF() && ($val = $rs->Fields(''))) {
    			$obj = DbFactory::makeObject($className);
      			$obj->fillEntity($val);
      			$result = $obj;
      		}
  		} else {
  			$result = array();
    		while ($rs && !$rs->EOF()) {
    			$val = $rs->Fields('');
    			$obj = DbFactory::makeObject($className);
      			$obj->fillEntity($val);
      			$result[] = $obj;
      			$rs->MoveNext();
      		}
  		}
		return $result;
	}
	
	/**
	* @return Array
	* @param String $sql_str
	* @param String $className
	* @desc Retrive array of objects determined in className
	*/
	public static function __selectObjects($sql_str, $className) {
		$db = SysContext::getConnection();
    	if (!$db) return false;
		
//print_r($sql_str);

  		$rs = $db->execute($sql_str);

		$result = array();
		while ($rs && !$rs->EOF()) {
			$val = $rs->Fields('');
  			$obj = DbFactory::makeObject($className);
      		$obj->fillEntity($val);
      		$result[] = $obj;
  			$rs->MoveNext();
  		}
		return $result;
	}
	
	public static function __selectAggregate($sql_str) {
		$db = SysContext::getConnection();
    	if (!$db) return false;
		
//print_r($sql_str);

  		$rs = $db->execute($sql_str);

		$result = array();
		while ($rs && !$rs->EOF()) {
			$val = $rs->Fields('');
  			$result[] = $val;
  			$rs->MoveNext();
  		}
		return $result;
	}
	
	public static function __executeQuery($sql_str) {
		$db = SysContext::getConnection();
    	if (!$db) return false;
		
  		$db->execute($sql_str);
	}
	
	public static function findAll($className, $local=null) {
		return self::__selectAllByCriteria(null, $className, $local);
	}
	
	public static function findById($id, $className, $local=null) {
		return self::__selectByCriteria(self::_get_ID($className)."='".$id."'", $className, $local);
	}
	
	

	// --
	// PHP is simply in implementation classes
	//
	var $className = "data.db.DbAbstractTable";
	const CLASS_NAME = "data.db.DbAbstractTable";
	
	const TB_TABLENAME	= "table";
	const TB_I			= 'tb';
	
	public static function _get_ID($className) {
		$o = getInstanceByClass($className);
		return $o->_get_ID();
	}
	
	public static function _get_FOREIGN($className1, $className2) {
		$o1 = getInstanceByClass($className1);
		$o2 = getInstanceByClass($className2);
		return $o1->_get_SUFFIX()."_id_".$o2->_get_SUFFIX();
	}
	
	public static function _get_TABLENAME($className=null) {
		if ($className!=null) {
			$o = getInstanceByClass($className);
			return $o->_get_TABLENAME();
		} else {
			return self::TB_TABLENAME;
		}
	}
	
	public static function _get_SUFFIX($className=null) {
		if ($className!=null) {
			$o = getInstanceByClass($className);
			return $o->_get_SUFFIX();
		} else {
			return self::TB_I;
		}
		
	}
}

$arrayOfInstances = array();

function &getInstanceByClass($className) {
	if (!isset($arrayOfInstances[$className])) {
		$clazz = import($className);
		if ($clazz) {
			$arrayOfInstances[$className] = new $clazz(true);
		} else {
			throw new Exception ('Driver not found');
		}
	}
	return $arrayOfInstances[$className];	
}
?>